package mc.support.web.action;

import mc.support.web.action.aware.Aware;
import mc.support.web.action.invoke.*;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA. <br/>
 * Author: Francis Yun    <br/>
 * Date: 2014-04-21  <br/>
 */
public class ActionInvokerRouter extends InvokerSupport {

    private static final byte[] lock0 = new byte[0];
    private static final byte[] lock1 = new byte[0];

    static {
        CommonActionInvoker commonActionInvoker = new CommonActionInvoker();
        InvokerMgr.registerInvoker( new FileUploadActionInvoker() );
        InvokerMgr.registerInvoker( new FileDownloadActionInvoker() );
        InvokerMgr.registerInvoker( commonActionInvoker );
        InvokerMgr.setDefaultInvoker(commonActionInvoker);
    }

    private ActionInvokerRouter() {}

    public static ActionInvokerRouter getInstance() {
        return SingleActionInvokerRouter.actionInvokerRouter;
    }

    private static class SingleActionInvokerRouter {
        private static ActionInvokerRouter actionInvokerRouter = new ActionInvokerRouter();
    }

    private Map<String, Class<?>> classCache = new ConcurrentHashMap<String, Class<?>>();
    private Map<Class<?>, Boolean> awareCache = new ConcurrentHashMap<>();

    public void invoke(ActionWrapper action, Map<String, Object> params) throws Exception {
        Class<?> actionClazz = getActionClass( action.getActionClsName() );
        Invoker<?> invoker = InvokerMgr.resolveInvoker(actionClazz);
        Object result;
        if ( isAware( actionClazz ) ) {
            Invoker<Aware> awaredInvoker = (Invoker<Aware>) invoker;
            result = awaredInvoker.invoke( (Aware)action.getActionInstance(), action, params );
        } else {
            result = invoker.invoke( null, action, params );
        }
        ResultWriter resultWriter = invoker.getResultWriter();
        resultWriter.writeResult( resultWriter, getHttpResp( params ), result );
    }

    private Class<?> getActionClass(String className) {
        Class<?> clazz = classCache.get( className );
        if ( null == clazz ) {
            synchronized ( lock0 ) {
                clazz = classCache.get( className );
                if ( null == clazz ) {
                    try {
                        clazz = Class.forName( className );
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException( "can't find Action class: " + className );
                    }
                }
            }
            classCache.put( className, clazz );
        }

        return clazz;
    }

    private Boolean isAware(Class<?> clazz) {
        Boolean isAware = awareCache.get( clazz );
        if ( null == isAware ) {
            synchronized ( lock1 ) {
                isAware = awareCache.get( clazz );
                if ( null == isAware ) {
                    isAware = Aware.class.isAssignableFrom(clazz);
                    awareCache.put( clazz, isAware );
                }
            }
        }
        return isAware;
    }

}
